package com.rain.shoppingscienceproj.http;

import android.content.Context;
import android.text.TextUtils;
import android.util.Log;

import com.rain.shoppingscienceproj.module.Address;
import com.rain.shoppingscienceproj.module.CarData;
import com.rain.shoppingscienceproj.module.Comment;
import com.rain.shoppingscienceproj.module.Goods;
import com.rain.shoppingscienceproj.module.GoodsDto;
import com.rain.shoppingscienceproj.module.LogisticsData;
import com.rain.shoppingscienceproj.module.Massage;
import com.rain.shoppingscienceproj.module.OrderBetweenDto;
import com.rain.shoppingscienceproj.module.StoreInfo;
import com.rain.shoppingscienceproj.module.UserInfo;
import com.rain.shoppingscienceproj.module.WeChatPayBean;

import java.io.File;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import okhttp3.Cache;
import okhttp3.ConnectionPool;
import okhttp3.MultipartBody;
import okhttp3.OkHttpClient;
import okhttp3.RequestBody;
import okhttp3.ResponseBody;
import okhttp3.logging.HttpLoggingInterceptor;
import retrofit2.Retrofit;
import retrofit2.adapter.rxjava.RxJavaCallAdapterFactory;
import retrofit2.converter.gson.GsonConverterFactory;
import rx.Observable;
import rx.Subscriber;
import rx.Subscription;
import rx.android.schedulers.AndroidSchedulers;
import rx.functions.Func1;
import rx.schedulers.Schedulers;

/**
 * RetrofitClient主要负责创建具体Retrofit，
 * 和调度分发请求。设置格式工厂。添加cookie同步，
 * 构建OkHttpClient，添加BaseUrl,对加密证书https
 * RetrofitClient
 * Created by Rain on 2017/6/13 0013.
 */

public class RetrofitClient {
    private static final int DEFAULT_TIMEOUT = 5;
    private BaseApiService apiService;
    private static OkHttpClient okHttpClient;
    public static String baseUrl = BaseApiService.Base_URL;
    private static Context mContext;
    private static RetrofitClient sNewInstance;

    private static Retrofit retrofit;
    private Cache cache = null;
    private File httpCacheDirectory;


    private static Retrofit.Builder builder = new Retrofit.Builder()
            .addConverterFactory(GsonConverterFactory.create())
            .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
            .baseUrl(baseUrl);


    private static OkHttpClient.Builder httpClient = new OkHttpClient.Builder()
            .addNetworkInterceptor(new HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.HEADERS))
            .connectTimeout(DEFAULT_TIMEOUT, TimeUnit.MINUTES);


    private static class SingletonHolder {
        private static RetrofitClient INSTANCE = new RetrofitClient(
                mContext);
    }


    public static RetrofitClient getInstance(Context context) {
        if (context != null) {
            mContext = context;
        }
        return SingletonHolder.INSTANCE;
    }

    public static RetrofitClient getInstance(Context context, String url) {
        if (context != null) {
            mContext = context;
        }

        return new RetrofitClient(context, url);
    }

    public static RetrofitClient getInstance(Context context, String url, Map<String, String> headers) {
        if (context != null) {
            mContext = context;
        }
        return new RetrofitClient(context, url, headers);
    }

    private RetrofitClient() {

    }


    private RetrofitClient(Context context) {

        this(context, baseUrl, null);
    }


    private RetrofitClient(Context context, String url) {

        this(context, url, null);
    }


    private RetrofitClient(Context context, String url, Map<String, String> headers) {

        if (TextUtils.isEmpty(url)) {
            url = baseUrl;
        }

        if (httpCacheDirectory == null) {
            httpCacheDirectory = new File(mContext.getCacheDir(), "tamic_cache");
        }

        try {
            if (cache == null) {
                cache = new Cache(httpCacheDirectory, 100 * 1024 * 1024);
            }
        } catch (Exception e) {
            Log.e("OKHttp", "Could not create http cache", e);
        }
        okHttpClient = new OkHttpClient.Builder()
                .addNetworkInterceptor(new HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY))
                .cookieJar(new NovateCookieManger(context))
//                .cache(cache)
//                .addInterceptor(new BaseInterceptor(headers))
//                .addInterceptor(new CaheInterceptor(context))
//                .addNetworkInterceptor(new CaheInterceptor(context))
                .connectTimeout(DEFAULT_TIMEOUT, TimeUnit.MINUTES)
                .writeTimeout(DEFAULT_TIMEOUT, TimeUnit.MINUTES)
                .connectionPool(new ConnectionPool(8, 3, TimeUnit.MINUTES))
                // 这里你可以根据自己的机型设置同时连接的个数和时间，我这里8个，和每个保持时间为10s
                .build();
        retrofit = new Retrofit.Builder()
                .client(okHttpClient)
                .addConverterFactory(GsonConverterFactory.create())
                .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
                .baseUrl(url)
                .build();
    }


    /**
     * ApiBaseUrl
     *
     * @param newApiBaseUrl
     */
    public static void changeApiBaseUrl(String newApiBaseUrl) {
        baseUrl = newApiBaseUrl;
        builder = new Retrofit.Builder()
                .addConverterFactory(GsonConverterFactory.create())
                .baseUrl(baseUrl);
    }

    /**
     * addcookieJar
     */
    public static void addCookie() {
        okHttpClient.newBuilder().cookieJar(new NovateCookieManger(mContext)).build();
        retrofit = builder.client(okHttpClient).build();
    }

    /**
     * ApiBaseUrl
     *
     * @param newApiHeaders
     */
    public static void changeApiHeader(Map<String, String> newApiHeaders) {

        okHttpClient.newBuilder().addInterceptor(new BaseInterceptor(newApiHeaders)).build();
        builder.client(httpClient.build()).build();
    }

    /**
     * create BaseApi  defalte ApiManager
     *
     * @return ApiManager
     */
    public RetrofitClient createBaseApi() {
        apiService = create(BaseApiService.class);
        return this;
    }


    /**
     * create you ApiService
     * Create an implementation of the API endpoints defined by the {@code service} interface.
     */
    public <T> T create(final Class<T> service) {
        if (service == null) {
            throw new RuntimeException("Api service is null!");
        }
        return retrofit.create(service);
    }

    public void code(String userPhone, Subscriber<ResponseBody> subscriber) {
        apiService.getCode(userPhone)
                .compose(schedulersTransformer())
                .compose(applySchedulers())
                .subscribe(subscriber);
    }

    public void registeruser(String userPhone, String code, Subscriber<ResponseBody> subscriber) {
        apiService.registeruser(userPhone, code)
                .compose(schedulersTransformer())
                .compose(applySchedulers())
                .subscribe(subscriber);
    }

    public void login(String mobile, String password, String deviceId, Subscriber<ResponseBody> subscriber) {
        apiService.login(mobile, password, deviceId, 0)
                .compose(schedulersTransformer())
                .compose(applySchedulers())
                .subscribe(subscriber);
    }

    public void userInfo(String userId, Subscriber<ResponseBody> subscriber) {
        apiService.userInfo(userId)
                .compose(schedulersTransformer())
                .compose(applySchedulers())
                .subscribe(subscriber);
    }

    public void delGoods(Map<String, String> map, Subscriber<ResponseBody> subscriber) {
        apiService.delGoods(map)
                .compose(schedulersTransformer())
                .compose(applySchedulers())
                .subscribe(subscriber);
    }

    public void forgetpwd(Map<String, String> map, Subscriber<ResponseBody> subscriber) {
        apiService.forgetpwd(map)
                .compose(schedulersTransformer())
                .compose(applySchedulers())
                .subscribe(subscriber);
    }

    public void mainDetail(String provinceCode, Subscriber<ResponseBody> subscriber) {
        apiService.mainDetail(provinceCode)
                .compose(schedulersTransformer())
                .compose(applySchedulers())
                .subscribe(subscriber);
    }


    /**
     * 注册
     *
     * @param map
     * @param subscriber
     */
    public void register(Map<String, String> map, Subscriber<ResponseBody> subscriber) {
        apiService.register(map)
                .compose(schedulersTransformer())
                .compose(applySchedulers())
                .subscribe(subscriber);
    }


    /**
     * 搜索
     *
     * @param map
     * @param subscriber
     */
    public Subscription secondSearch(Map<String, String> map, Subscriber<List<Goods>> subscriber) {
        return apiService.secondSearch(map)
                .compose(schedulersTransformer())
                .compose(transformer())
                .subscribe(subscriber);
    }

    public Subscription messageList(Map<String, String> map, Subscriber<List<Massage>> subscriber) {
        return apiService.messageList(map)
                .compose(schedulersTransformer())
                .compose(transformer())
                .subscribe(subscriber);
    }

    /**
     * 分类下列表
     *
     * @param map
     * @param subscriber
     */
    public Subscription typeGoods(Map<String, String> map, Subscriber<List<Goods>> subscriber) {
        return apiService.typeGoods(map)
                .compose(schedulersTransformer())
                .compose(transformer())
                .subscribe(subscriber);
    }

    /**
     * 用户信息
     *
     * @param map
     * @param subscriber
     */
    public Subscription userInfo(Map<String, String> map, Subscriber<UserInfo> subscriber) {
        return apiService.userInfo(map)
                .compose(schedulersTransformer())
                .compose(transformer())
                .subscribe(subscriber);
    }

    /**
     * 修改2
     * 删除3
     * 新增1
     *
     * @param map
     * @param subscriber
     * @return
     */
    public void upaAdress(Map<String, String> map, Subscriber<ResponseBody> subscriber) {
        apiService.upaAdress(map)
                .compose(schedulersTransformer())
                .compose(applySchedulers())
                .subscribe(subscriber);
    }

    public void delAdress(Map<String, String> map, Subscriber<ResponseBody> subscriber) {
        apiService.delAdress(map)
                .compose(schedulersTransformer())
                .compose(applySchedulers())
                .subscribe(subscriber);
    }

    public void addAdress(Map<String, String> map, Subscriber<ResponseBody> subscriber) {
        apiService.addAdress(map)
                .compose(schedulersTransformer())
                .compose(applySchedulers())
                .subscribe(subscriber);
    }


    public Subscription queryApproveAddress(Map<String, String> map, Subscriber<Address> subscriber) {
        return apiService.queryApproveAddress(map)
                .compose(schedulersTransformer())
                .compose(transformer())
                .subscribe(subscriber);
    }

    /**
     * 地址列表
     *
     * @param map
     * @param subscriber
     */
    public Subscription queryAddress(Map<String, String> map, Subscriber<List<Address>> subscriber) {
        return apiService.queryAddress(map)
                .compose(schedulersTransformer())
                .compose(transformer())
                .subscribe(subscriber);
    }

    /**
     * 上传头像
     *
     * @param map
     * @param files
     * @param subscriber
     */
    public void upPhoto(Map<String, RequestBody> map, MultipartBody.Part[] files, Subscriber<ResponseBody> subscriber) {
        apiService.upPhoto(map, files)
                .compose(schedulersTransformer())
                .compose(applySchedulers())
                .subscribe(subscriber);
    }

    /**
     * 修改用户
     *
     * @param map
     * @param subscriber
     */
    public void upUserData(Map<String, String> map, Subscriber<ResponseBody> subscriber) {
        apiService.updateuser(map)
                .compose(schedulersTransformer())
                .compose(applySchedulers())
                .subscribe(subscriber);
    }

    /**
     * 反馈
     *
     * @param map
     * @param subscriber
     */
    public void feedBack(Map<String, String> map, Subscriber<ResponseBody> subscriber) {
        apiService.feedBack(map)
                .compose(schedulersTransformer())
                .compose(applySchedulers())
                .subscribe(subscriber);
    }

    public void comment(Map<String, String> map, Subscriber<ResponseBody> subscriber) {
        apiService.comment(map)
                .compose(schedulersTransformer())
                .compose(applySchedulers())
                .subscribe(subscriber);
    }


    public Subscription queryCarList(Map<String, String> map, Subscriber<List<CarData>> subscriber) {
        return apiService.queryCarList(map)
                .compose(schedulersTransformer())
                .compose(transformer())
                .subscribe(subscriber);
    }

    public void addCar(Map<String, String> map, Subscriber<ResponseBody> subscriber) {
        apiService.addCar(map)
                .compose(schedulersTransformer())
                .compose(applySchedulers())
                .subscribe(subscriber);
    }

    public void upCar(Map<String, String> map, Subscriber<ResponseBody> subscriber) {
        apiService.upCar(map)
                .compose(schedulersTransformer())
                .compose(applySchedulers())
                .subscribe(subscriber);
    }

    public void delCar(Map<String, String> map, Subscriber<ResponseBody> subscriber) {
        apiService.delCar(map)
                .compose(schedulersTransformer())
                .compose(applySchedulers())
                .subscribe(subscriber);
    }

    public Subscription goodsDetaile(String goodsId, Subscriber<GoodsDto> subscriber) {
        return apiService.goodsDetaile(goodsId)
                .compose(schedulersTransformer())
                .compose(transformer())
                .subscribe(subscriber);
    }


    public void submitOneOrder(Map<String, String> map, Subscriber<ResponseBody> subscriber) {
        apiService.submitOneOrder(map)
                .compose(schedulersTransformer())
                .compose(applySchedulers())
                .subscribe(subscriber);
    }
    public void submitListOrder(Map<String, String> map, Subscriber<ResponseBody> subscriber) {
        apiService.submitListOrder(map)
                .compose(schedulersTransformer())
                .compose(applySchedulers())
                .subscribe(subscriber);
    }

    public Subscription submitOrder(Map<String, String> map, Subscriber<WeChatPayBean> subscriber) {
        return apiService.submitOrder(map)
                .compose(schedulersTransformer())
                .compose(transformer())
                .subscribe(subscriber);
    }

    public Subscription pay(Map<String, String> map, Subscriber<WeChatPayBean> subscriber) {
        return apiService.pay(map)
                .compose(schedulersTransformer())
                .compose(transformer())
                .subscribe(subscriber);
    }

    public Subscription orderStatus(Map<String, String> map, Subscriber<List<OrderBetweenDto>> subscriber) {
        return apiService.orderStatus(map)
                .compose(schedulersTransformer())
                .compose(transformer())
                .subscribe(subscriber);
    }

    public void sureOrder(Map<String, String> map, Subscriber<ResponseBody> subscriber) {
        apiService.sureOrder(map)
                .compose(schedulersTransformer())
                .compose(applySchedulers())
                .subscribe(subscriber);
    }

    public void cancelOrder(Map<String, String> map, Subscriber<ResponseBody> subscriber) {
        apiService.cancelOrder(map)
                .compose(schedulersTransformer())
                .compose(applySchedulers())
                .subscribe(subscriber);
    }


    public Subscription storeInfo(String shopId, Subscriber<StoreInfo> subscriber) {
        return apiService.storeInfo(shopId)
                .compose(schedulersTransformer())
                .compose(transformer())
                .subscribe(subscriber);
    }



    public Subscription commentList(String goodsId, String page, Subscriber<List<Comment>> subscriber) {
        return apiService.commentList(goodsId, page)
                .compose(schedulersTransformer())
                .compose(transformer())
                .subscribe(subscriber);
    }

    public Subscription orderdetails(Map<String, String> map, Subscriber<OrderBetweenDto> subscriber) {
        return apiService.orderdetails(map)
                .compose(schedulersTransformer())
                .compose(transformer())
                .subscribe(subscriber);
    }


    public Subscription queryordermanage(String orderId, Subscriber<LogisticsData> subscriber) {
        return apiService.queryordermanage(orderId)
                .compose(schedulersTransformer())
                .compose(transformer())
                .subscribe(subscriber);
    }


    public void queryorderbystatus(String orderId, Subscriber<ResponseBody> subscriber) {
        apiService.queryorderbystatus(orderId)
                .compose(schedulersTransformer())
                .compose(applySchedulers())
                .subscribe(subscriber);
    }


    public void querygoodsdetils(String goodsIds, Subscriber<ResponseBody> subscriber) {
        apiService.querygoodsdetils(goodsIds)
                .compose(schedulersTransformer())
                .compose(applySchedulers())
                .subscribe(subscriber);
    }

    public Observable.Transformer schedulersTransformer() {
        return new Observable.Transformer() {


            @Override
            public Object call(Object observable) {
                return ((Observable) observable).subscribeOn(Schedulers.io())
                        .unsubscribeOn(Schedulers.io())
                        .observeOn(AndroidSchedulers.mainThread());
            }

           /* @Override
            public Observable call(Observable observable) {
                return observable.subscribeOn(Schedulers.io())
                        .unsubscribeOn(Schedulers.io())
                        .observeOn(AndroidSchedulers.mainThread());
            }*/
        };
    }

    <T> Observable.Transformer<T, T> applySchedulers() {
        return (Observable.Transformer<T, T>) schedulersTransformer();
    }


    public <T> Observable.Transformer<BaseResponse<T>, T> transformer() {

        return new Observable.Transformer() {

            @Override
            public Object call(Object observable) {
                return ((Observable) observable).map(new HandleFuc<T>()).onErrorResumeNext(new HttpResponseFunc<T>());
            }
        };
    }

    public <T> Observable<T> switchSchedulers(Observable<T> observable) {
        return observable.subscribeOn(Schedulers.io())
                .unsubscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread());
    }

    private static class HttpResponseFunc<T> implements Func1<Throwable, Observable<T>> {
        @Override
        public Observable<T> call(Throwable t) {
            return Observable.error(ExceptionHandle.handleException(t));
        }
    }

    private class HandleFuc<T> implements Func1<BaseResponse<T>, T> {
        @Override
        public T call(BaseResponse<T> response) {
            if (!response.isOk())
                throw new RuntimeException(response.getStatus() + "" + response.getMsg() != null ? response.getMsg() : "");
            return response.getData();
        }
    }

    /**
     * DownSubscriber
     *
     * @param <ResponseBody>
     */
    class DownSubscriber<ResponseBody> extends Subscriber<ResponseBody> {
        CallBack callBack;

        public DownSubscriber(CallBack callBack) {
            this.callBack = callBack;
        }

        @Override
        public void onStart() {
            super.onStart();
            if (callBack != null) {
                callBack.onStart();
            }
        }

        @Override
        public void onCompleted() {
            if (callBack != null) {
                callBack.onCompleted();
            }
        }

        @Override
        public void onError(Throwable e) {
            if (callBack != null) {
                callBack.onError(e);
            }
        }

        @Override
        public void onNext(ResponseBody responseBody) {
            DownLoadManager.getInstance(callBack).writeResponseBodyToDisk(mContext, (okhttp3.ResponseBody) responseBody);
        }
    }

}
